const User = require('../lib/object/user')
const { BusinessLogicError } = require('../errors/application-error')
const knex = require('../lib/db')
const jwt = require('jsonwebtoken')
const { isProduction } = require('../constant')
const UserService = require('./user')

async function refreshToken(refreshToken, ip, region, userAgent, requestId, type, port) {
  const tokenDetails = await this.queryBus.execute(new TokensByRefreshTokenQuery(refreshToken))
  if (!tokenDetails) {
    throw new NotFoundException('Refresh token not found.')
  }

  await this.jwtService.verifyAsync(tokenDetails.refreshToken, {
    secret: this.securityConfig.refreshJwtSecret
  })

  const tokensAggregate = new TokensEntity(tokenDetails)

  await tokensAggregate.refreshTokenCheck()

  const tokens = await this.generateAccessToken(tokensAggregate.userId, tokenDetails.username, tokenDetails.domain)

  tokensAggregate.apply(
    new TokenGeneratedEvent(
      tokens.token,
      tokens.refreshToken,
      tokensAggregate.userId,
      tokensAggregate.username,
      tokensAggregate.domain,
      ip,
      region,
      userAgent,
      requestId,
      type,
      port
    )
  )

  this.publisher.mergeObjectContext(tokensAggregate)
  tokensAggregate.commit()

  return tokens
}

async function execPasswordLogin(dto) {
  const { identifier, password } = dto
  // 使用knex查询一个用户
  const user = (await knex('sys_user').where({ username: identifier }))?.[0]
  if (!user) {
    throw new NotFoundException('User not found.')
  }
  const userAggregate = new User(user)
  const loginResult = await userAggregate.loginUser(password)

  if (!loginResult.success) {
    throw new BusinessLogicError('USER_LOGIN_ERROR')
  }

  const tokens = generateAccessToken(user.id, user.username, user.domain)

  return tokens
}

function generateAccessToken(userId, username, domain) {
  const payload = {
    uid: userId,
    username: username,
    domain: domain
  }

  const accessToken = jwt.sign(
    payload,
    process.env.JWT_SECRET, // token加密密钥
    { expiresIn: isProduction ? '2h' : '30d' }
  )

  const refreshToken = jwt.sign(
    payload,
    process.env.JWT_SECRET, // token加密密钥
    { expiresIn: '30d' }
  )

  return { token: accessToken, refreshToken }
}

async function getProfile(userId) {
  // 获取当前用户信息, 从 sys_user 表中查询,roles 字段是用户角色列表,关联sys_role、sys_user_role表获取角色信息
  const user = await UserService.findOne({ id: userId })
  const roleCodes = await knex('sys_user_role').where('user_id', userId).join('sys_role', 'sys_user_role.role_id', 'sys_role.id').select('sys_role.code').pluck('code')
  return {
    userId: user.id,
    userName: user.username,
    roles: roleCodes
  }
}

module.exports = {
  refreshToken,
  execPasswordLogin,
  generateAccessToken,
  getProfile
}
